Create a Report with ZK using iReport and JasperReports

From Documentation
DocumentationSmall Talks2012AprilCreate a Report with ZK using iReport and JasperReports
Create a Report with ZK using iReport and JasperReports

Author
Jimmy Shiau & Vincent Jian, Engineer, Potix Corporation
Date
April 19, 2012
Version
ZK 5/6, iReport 4.5.0, JasperReports 4.5.0

Introduction

JasperReports is a powerful reporting engine which supports various formats including pdf, html, rtf, xls, doc etc, ZK has integrated JasperReports to leverage these powers for ease of reporting. This article demonstrates how you can export your ZK-powered page to a desired JasperReport format and layout, designed by iReport, a GUI based design tool for JasperReport.

Prerequisite

JasperReports

ZK 5.0.8/6 and later have integrated jasperreport version 4.0.1 and you can use the jasperreports.jar packaged within ZK directly. If you want to use the latest version of jasperreport, you can download it from http://sourceforge.net/projects/jasperreports/files/jasperreports/ Note that ZK JasperReport Integration is part of ZK PE/EE so please download ZK PE/EE or the evaluation copy of ZK PE/EE.

iReport

Download iReport from JasperForge and install. Notice that the version of iReport should exactly match with the version of JasperReports.

Scenario One: Create Report with Custom Data Source

In this sample, we assume you have a ZK page with two grids which contain custom data source and you wish to export these grids to a PDF/RTF table using JasperReports.

The ZK page

The data page we demonstrate here is a Food List with various categories. The list is displayed as two grid components.

IReport-sample1-01.png

Necessary classes are as follows:

  • Food bean.
  • public class Food {
    	private String Category;
    	private String Name;
    	private String TopNutrients;
    	private Integer DailyPercent;
    	private Integer Calories;
    	private String Quantity;
    	public Food(String category, String name, String topNutrients, Integer dailyPercent, Integer calories, String quantity) {
    		Category = category;
    		Name = name;
    		TopNutrients = topNutrients;
    		DailyPercent = dailyPercent;
    		Calories = calories;
    		Quantity = quantity;
    	}
    	// getters and setters
    }
    
  • Food Data Access Object.
  • public class FoodData {
    	private static List<Food> foods = new ArrayList<Food>();
    	static {
    		foods.add(new Food("Vegetables", "Asparagus", "Vitamin K", 115, 43, "1 cup - 92 grams"));
    		foods.add(new Food("Vegetables", "Beets", "Folate", 33, 74, "1 cup - 170 grams"));
    		// other foods
    	}
    	public static List<Food> getFoodsByCategory(String category) {
    		List<Food> somefoods = new ArrayList<Food>();
    		for (Iterator<Food> i = foods.iterator(); i.hasNext();) {
    			Food tmp = i.next();
    			if (tmp.getCategory().equals(category))
    			somefoods.add(tmp);
    		}
    		return somefoods;
    	}
    }
    
  • Data set by model and render by RowRenderer.
  • public class ReportSample1Composer extends GenericForwardComposer {
    	private Grid vegetable;
    	private Grid seafood;
    	private List<Food> vegetables = FoodData.getFoodsByCategory("Vegetables");
    	private List<Food> seafoods = FoodData.getFoodsByCategory("Seafood");
    	public void doAfterCompose(Component comp) throws Exception {
    		super.doAfterCompose(comp);
    		RowRenderer renderer = new RowRenderer() {
    			public void render(Row row, Object data) throws Exception {
    				Food food = (Food) data;
    				new Label(food.getName()).setParent(row);
    				new Label(food.getTopNutrients()).setParent(row);
    				new Label(food.getDailyPercent() + "").setParent(row);
    				new Label(food.getCalories() + "").setParent(row);
    				new Label(food.getQuantity()).setParent(row);
    			}
    		};
    		vegetable.setModel(new ListModelList(vegetables));
    		vegetable.setRowRenderer(renderer);
    		seafood.setModel(new ListModelList(seafoods));
    		seafood.setRowRenderer(renderer);
    	}
    }
    

Design the report layout by iReport

Now we are ready to design our report with the following steps:

  1. First, execute iReport and set classpath that contains the Food class
  2. Then, create a new empty report with Blank A4 template and change the language properties to Java
  3. If you want to specify report title dynamically in Java code instead of static text, you can add a parameter named "reportTitle" and specify "Parameter class" to java.lang.String
  4. Add two parameters named "vegetableDataSource" and "seafoodDataSource", then specify "Parameter class" of both parameters to net.sf.jasperreports.engine.JRDataSource
  5. Add an empty data set in the report named "FoodDataSet"
  6. Now, design the report layout for vegetable grid in the ZUL page
    • Add fields in "FoodDataSet" according to Food bean properties
    • Add "Table" element to "Detail 1" band and follow "Table Wizard"
      1. Create a table from "FoodDataSet" and set columns to "5", then click "Next"
      2. IReport-sample1-11.png
      3. Select the fields you want to show in the table, then click "Next"
      4. IReport-sample1-12.png
      5. Select "Use a JRDatasource expression" option and type $P{vegetableDataSource} in the expression, then click "Next"
      6. IReport-sample1-13.png
      7. Design the table style, then click "Finish"
      8. IReport-sample1-14.png
    • Tweak the report and table layout as you wish
  7. Then, design the report layout for seafood grid in the ZUL page
    • Add another Detail band in the report
    • IReport-sample1-15.png
    • Repeat step 6-2 to add another table in "Detail 2" band
  8. Compile MyReport and copy MyReport.jasper file to your web project
  9. IReport-sample1-16.png

Use ZK JasperReports in your web project

Now we need to create data source instance and set ZK JasperReports settings as follows:

  1. Create a DataSource instance that implements net.sf.jasperreports.engine.JRDataSource
  2. public class DataSource implements JRDataSource {
    	private List<Food> foods;
    	private int index = -1;
    	public DataSource(List<Food> foods) {
    		super();
    		this.foods = foods;
    	}
    	public Object getFieldValue(JRField field) throws JRException {
    		String fieldName = field.getName();
    		Food food = foods.get(index);
    		if ("name".equals(fieldName)) {
    			return food.getName();
    		} else if ("topNutrients".equals(fieldName)) {
    			return food.getTopNutrients();
    		} else if ("dailyPercent".equals(fieldName)) {
    			return food.getDailyPercent();
    		} else if ("calories".equals(fieldName)) {
    			return food.getCalories();
    		} else if ("quantity".equals(fieldName)) {
    			return food.getQuantity();
    		}
    		return "";
    	}
    	public boolean next() throws JRException {
    		return ++index < foods.size();
    	}
    }
    
  3. Create a report.zul page which contains jasperreport component
  4. <window title="Report" border="normal" width="800px" height="600px" closable="true" mode="modal">
    	<jasperreport id="report" height="600px" />
    </window>
    
  5. Create two buttons in the index.zul page, and let them forward onClick event with a string to the onShowReport event in the composer.
  6. <hlayout>
    	Export Report to <button label="PDF" forward='onShowReport(pdf)' /><button label="RTF" forward='onShowReport(rtf)' />
    </hlayout>
    
    public void onShowReport(ForwardEvent event) {
    	String type = event.getData().toString();
    	String path = page.getDesktop().getWebApp().getRealPath("/sample1");
    	Window win = (Window) Executions.createComponents("report.zul", null, null);
    	
    	Map<String, Object> params = new HashMap<String, Object>();
    	// Add parameters used in this report
    	params.put("reportTitle", "My Food List");
    	params.put("vegetableDataSource", new DataSource(vegetables));
    	params.put("seafoodDataSource", new DataSource(seafoods));
    	
    	Jasperreport report = (Jasperreport) win.getFellow("report");
    	report.setType(type);
    	report.setSrc(path + "/MyReport.jasper");
    	report.setParameters(params);
    }
    
  7. Now we have finished all the settings, the following image shows what the resulting report looks like:
  8. IReport-sample1-17.png

Scenario Two: Create Sub-Report with Bean Collection Data Source

In this sample, we assume you have a ZK page with a Master-Detail grid which contains a bean collection data source where each bean has another bean collection data. And you wish to export the grid to a PDF/RTF sub-report table using JasperReports.

The ZK page

The data page we demonstrate here contains a Person List with each person's favorite Food list. The list is displayed as a Master-Detail grid component.

IReport-sample2-01.png

Necessary classes are as follows:

  • Person bean with a list of Food bean in scenario one.
  • public class Person {
    	private String name;
    	private Integer age;
    	private List<Food> foods = new ArrayList<Food>();
    	
    	public Person(String name, Integer age, List<Food> foods) {
    		this.name = name;
    		this.age = age;
    		this.foods = foods;
    	}
    	// getters and setters
    }
    
  • Person Data Access Object.
  • public class PersonData {
    	private static List<Person> people = new ArrayList<Person>();
    	static {
    		people.add(new Person("Tom Riddle", 50, FoodData.getFoodsByCategory("Grains")));
    		people.add(new Person("Harry Potter", 20, FoodData.getFoodsByCategory("Fruits")));
    		people.add(new Person("Ron Wesley", 21, FoodData.getFoodsByCategory("Seafood")));
    	}
    	
    	public static List<Person> getPeople() {
    		return people;
    	}
    }
    
  • Use data binding to show the data page in index.zul page.
  • <?init class="org.zkoss.zkplus.databind.AnnotateDataBinderInit" ?>
    <zk>
    <window id="win" title="JasperReports Sample 1" border="normal" apply="web.ui.ctrl.ReportSample2Composer">
    	<grid id="people" model="@{win$composer.personList}">
    		<columns>
    			<column width="40px" />
    			<column label="Name" />
    			<column label="Age" />
    		</columns>
    		<rows>
    			<row self="@{each='person'}" value="@{person}">
    				<detail open="true">
    					<grid id="vegetable" model="@{person.foods}">
    						<columns>
    							<column label="cagetory" />
    							<column label="Name" />
    							<!-- other Food properties -->
    						</columns>
    						<rows>
    							<row self="@{each='food'}" value="@{food}">
    								<label value="@{food.category}"/>
    								<label value="@{food.name}" />
    								<!-- other Food properties -->
    							</row>
    						</rows>
    					</grid>
    				</detail>
    				<label value="@{person.name}" />
    				<label value="@{person.age}" />
    			</row>
    		</rows>
    	</grid>
    </window>
    </zk>
    

Design the report layout by iReport

Now we are ready to design our report with the following steps:

  1. Execute iReport and set classpath that contains Person.class and Food.class
  2. Create a new report by iReport
    • Set data source of the report
    • Design the report and drag all Fields in Detail band
    • Remove Field value "foods" as the food bean data collection will be listed in sub-report
    • IReport-sample2-06.png
  3. Adding sub-report element in Detail band will trigger iReport wizard to create a new report
    • Press next directly in the first 5 steps of the wizard, and in step 6 of the wizard, remember to select "Store the directory name in a parameter" option.
    • IReport-sample2-07.png
    • In step 7 of the wizard, choose "Use a JRDataSource expression" option and click the edit icon
    • IReport-sample2-08.png
    • Type new net.sf.jasperreports.engine.data.JRBeanCollectionDataSource($F{foods}) in Expression editor then click Apply
    • Repeat step 2-1 to set data source of sub-report with Food.class bean and design the sub-report style.
  4. Edit "Default Value Expressions" attribute of the parameter SUBREPORT_DIR in main-report using an empty string "" for web project
  5. Compile report and copy both *.jasper files to your web project
  6. Right-Click MainReport and execute "Compile Report" to compile the report. It will also compile sub-report automatically.
    IReport-sample2-11.png

    Use ZK JasperReports in your web project

    Now we can set ZK JasperReports settings as follows:

    1. Create a report.zul page contains jasperreport component
    2. <window title="Report" border="normal" width="800px" height="600px" closable="true" mode="modal">
      	<jasperreport id="report" height="600px" />
      </window>
      
    3. Create two buttons in the index.zul page, and let them forward onClick event with a string to the onShowReport event in the composer.
    4. <hlayout>
      	Export Report to <button label="PDF" forward='onShowReport(pdf)' /><button label="RTF" forward='onShowReport(rtf)' />
      </hlayout>
      
      public void onShowReport(ForwardEvent event) {
      	String type = event.getData().toString();
      	// get the absolute path that contains *.jasper files
      	String path = page.getDesktop().getWebApp().getRealPath("/sample2");
      	Window win = (Window) Executions.createComponents("report.zul", null, null);
      	// set parameters used in the main-report
      	Map<String, Object> params = new HashMap<String, Object>();
      	params.put("SUBREPORT_DIR", path + "/");
      	// set jasperreport component attributes
      	Jasperreport report = (Jasperreport) win.getFellow("report");
      	report.setType(type);
      	report.setSrc(path + "/MainReport.jasper");
      	report.setParameters(params);
      	report.setDatasource(new JRBeanCollectionDataSource(PersonData.getPeople()));
      }
      
    5. Result
    6. IReport-sample2-12.png

    See Also

    • To export your report to a different format other than PDF and RTF, please refer to support type list.
    • To embed different fonts in the report, please refer to this blog.

    Download

    Download the source code and war file of the two samples illustrated above at: jasperreport.zip


    Comments



    Copyright © Potix Corporation. This article is licensed under GNU Free Documentation License.